﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WorkRecorder.Business;
using System.Collections.ObjectModel;
using System.Reflection;
using System.ComponentModel;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using WorkRecorder.Helpers.Converters;
using WorkRecorder.Helpers;
using System.Windows.Threading;
using System.Xml;
using System.Threading;
using System.Globalization;
using System.Windows.Markup;
using System.Diagnostics;
using System.Configuration;
using System.IO;
using System.Deployment.Application;
 
namespace WorkRecorder
{ 
    /// <summary>
    /// Helps keep track of time by storing history of worked events
    /// </summary>   
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
         
        #region New record commands and related methods

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CanExecuteThemeItemSelection(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
            e.Handled = true;
        }

        /// <summary>
        /// Get selected language and writes settings to app.config file, restarts program with new settings
        /// and closes current program.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ThemeItemSelectionExecuted(object sender, ExecutedRoutedEventArgs e)
        {
 
            var mi = e.Parameter as MenuItem;
            var dc = mi.DataContext;

            if (dc != null)
            {
                var xe = dc as XmlElement;
                var atts = xe.Attributes;
                var themePath = atts["path"].Value;

                //if default system theme, remove theme if it exists
                if (string.IsNullOrEmpty(themePath) || (themePath != null && themePath.Equals("default")))
                {
                    //if theme exists (occurrs for all non default options), remove it
                    var dicts = Application.Current.Resources.MergedDictionaries;
                    if (dicts != null && dicts.Count == 2)
                    {
                        dicts.RemoveAt(1);
                    }
                }
                else
                {
                    //load selected theme
                    Uri rescoursePath = new Uri("Pack://application:,,,/WorkRecorder;Component/" + themePath, UriKind.Absolute);
                    ResourceDictionary rd = new ResourceDictionary();
                    rd.Source = rescoursePath;
                    //this.Resources = rd;
                    var dicts = Application.Current.Resources.MergedDictionaries;
                    if (dicts != null && dicts.Count == 2)
                    {
                        dicts[1] = rd;
                    }
                    else
                    {
                        Application.Current.Resources.MergedDictionaries.Add(rd);
                    }
                }
                //update default settings for next application restart
                UpdateAppConfig("defaultTheme", themePath);


                //reset menuitem to unchecked
                if (mi != null) { mi.IsChecked = false; }
            }
            e.Handled = true;

        }

        /// <summary>
        /// Updates program.exe.config application - does not work in debug mode
        /// </summary>
        /// <param name="newCulture"></param>
        private void UpdateAppConfig(string key, string settingsValue)
        {
            //write new desired  appSettings so restarted application will use new setting
            Configuration config =  ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

            config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            config.AppSettings.Settings[key].Value = settingsValue;
            config.Save(ConfigurationSaveMode.Modified);
            ConfigurationManager.RefreshSection("appSettings");

        }


 

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CanExecuteLanguageItemSelection(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
            e.Handled = true;
        }

        /// <summary>
        /// Get selected language and writes settings to app.config file, restarts program with new settings
        /// and closes current program.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LanguageItemSelectionExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            var mi = e.Parameter as MenuItem;
            var dc = mi.DataContext;

            var selectedCulture = "";
            //if menuitem has datacontext, get attributes 
            if(dc != null)
            {
                var xe = dc as XmlElement;
                var atts = xe.Attributes;
                var cul = atts["culture"].Value;
                selectedCulture = cul;

            }
            
            //If selected culture is different than current culture, update application config file
            //and restart application, else do not change assembly
            if ((dc != null && !string.IsNullOrEmpty(selectedCulture)) && !Thread.CurrentThread.CurrentCulture.Name.Equals(selectedCulture) && ShowMessageBox(this.FindResource("messageTitleWarning").ToString(), this.FindResource("messageChangeCulture").ToString(), MessageBoxButton.OKCancel))
            {
                //modify the default language to be loaded to reflect language selection
                UpdateAppConfig("defaultLanguage", selectedCulture);                
                RestartProgram();
                //Environment.Exit(0);
            }
 
            //unchecked previously checked menuitem
            if (mi != null) { mi.IsChecked = false; }
            
            e.Handled = true;

        }



        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CanExecuteClear(object sender, CanExecuteRoutedEventArgs e)
        {
            bool b = (!String.IsNullOrEmpty(e.Parameter as string) || (MyWorkType != null && !String.IsNullOrEmpty(MyWorkType.Value as string)));

            //if timer not started and work type is selected or comments not null or empty, can clear fields
            e.CanExecute = (timer != null && !timer.IsEnabled && b) ? true : false;

            e.Handled = true;

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ClearExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            ClearNewRecordFields();
            e.Handled = true;
        }

        /// <summary>
        /// Clears UI fields back to defaults after record has been saved or clear fields button clicked
        /// </summary>
        private void ClearNewRecordFields()
        {
            //clear UI after save for new record
            this.MyComments = null;
            this.MyRecord = null;
            this.MyTimeElapsed = new TimeSpan(0);
            this.MyStartTime = new DateTime();
            this.canSaveRecord = false;
            this.MyWorkType = null;

        }

        //used to track state of save button. if timer is not active
        //but has been started and stopped, canSaveRecord is true
        private bool canSaveRecord;

        /// <summary>
        /// Saves record if canSaveRecord true and timer isn't running
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CanExecuteSave(object sender, CanExecuteRoutedEventArgs e)
        {
            if (timer != null)
            {
                e.CanExecute = !timer.IsEnabled && canSaveRecord;
            }

            e.Handled = true;

        }

        /// <summary>
        /// Adds new record and saves to disk
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SaveExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            //if record doesn't already exist, add it, otherwise duplicate records
            if (!RecordSet.Contains(MyRecord))
            {
                RecordSet.Add(MyRecord);

            }
            else
            {
                //existing record, update before saving
                var r = RecordSet.Single(x => x == MyRecord) as Record;
                r.EndTime = DateTime.Now; 
                r.TimeElapsed = MyTimeElapsed.Ticks;
                r.RType = this.MyWorkType.Value;
                r.Comments = this.MyComments;
            }

            //rebind
            recordsListView.DataContext = null;
            recordsListView.DataContext = this;
            LoadChartData();
            SaveRecords();
            //ClearNewRecordFields();
            e.Handled = true;

        }
         
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CanExecuteStop(object sender, CanExecuteRoutedEventArgs e)
        {
            //if timer is started, then can stop
            e.CanExecute = timer.IsEnabled;
             
            e.Handled = true;

        }

        /// <summary>
        /// If stopped, get current time and total time elapsed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void StopExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            MyRecord.EndTime = DateTime.Now;
            MyRecord.TimeElapsed = MyTimeElapsed.Ticks; //MyRecord.EndTime.Subtract(MyRecord.StartTime).Ticks;
            timer.Stop();
            canSaveRecord = true;
            e.Handled = true;

        }

        /// <summary>
        /// If work type in combo box is selected and some comments in comments box have data,
        /// can save is activated
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CanExecuteStart(object sender, CanExecuteRoutedEventArgs e)
        { 
            //if textbox is not empty and work type is selected, regardless of order chosen
            e.CanExecute = (!String.IsNullOrEmpty(e.Parameter as string) && (MyWorkType != null && !String.IsNullOrEmpty(MyWorkType.Value as string)));
            e.Handled = true;

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void StartExecuted(object sender, ExecutedRoutedEventArgs e)
        {

            e.Handled = true;


            //if new record, time elapsed will be at zero - create new record.
            //if not zero, timer has only been stopped and start restarts timer
            //causing pause-like effect
            //Savign record or clearing record resets timer to 0
            if (MyTimeElapsed.Ticks == 0)
            {
                MyRecord = new Record();                 
                this.MyStartTime = DateTime.Now;
                this.MyRecord.StartTime = this.MyStartTime;
                this.MyRecord.RType = this.MyWorkType.Value;
                this.MyRecord.Comments = this.MyComments;
               
                //timer = new DispatcherTimer();
                timer.Interval = new TimeSpan(0, 0, 0, 1);
                //timer.Tick += timer1_Tick;
                timer.Start();
                
            } 
            else if (timer != null)
            {
                timer.Start();

            }

            canSaveRecord = false;
        }

        /// <summary>
        /// Timer for time elapsed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            var interval = (sender as DispatcherTimer).Interval;

            long ticks = MyTimeElapsed.Ticks + new TimeSpan(interval.Ticks).Ticks;
            MyTimeElapsed = new TimeSpan(ticks);

        }
        #endregion


        #region Properties

        public string DeploymentPath { get; set; }


        public event PropertyChangedEventHandler PropertyChanged;


        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }


        private ObservableCollection<Record> recordSet = new ObservableCollection<Record>();

        public ObservableCollection<Record> RecordSet
        {
            get { return recordSet; }
            set
            {
                recordSet = value;
                //reload chart data on recordset renewal
                LoadChartData();
                this.NotifyPropertyChanged("RecordSet");
            }
        }


 


        private ObservableCollection<WorkHistory> history = new ObservableCollection<WorkHistory>();

        public ObservableCollection<WorkHistory> History
        {
            get { return history; }
            set
            {
                history = value;
                this.NotifyPropertyChanged("History");
            }
        }


        private string recordsPath;

        public string RecordsPath
        {
            get { return recordsPath; }
            set { recordsPath = value; }
        }

        private string myComments;

        public string MyComments
        {
            get { return myComments; }
            set
            {
                myComments = value;
                this.NotifyPropertyChanged("MyComments");
            }
        }

        private XmlAttribute myWorkType;

        public XmlAttribute MyWorkType
        {
            get { return myWorkType; }
            set
            {
                myWorkType = value;
                this.NotifyPropertyChanged("MyWorkType");
            }
        }

        private DateTime myStartTime;

        public DateTime MyStartTime
        {
            get { return myStartTime; }
            set { myStartTime = value; this.NotifyPropertyChanged("MyStartTime"); }
        }

        private DispatcherTimer timer = new DispatcherTimer();


        private TimeSpan myTimeElapsed;

        public TimeSpan MyTimeElapsed
        {
            get { return myTimeElapsed; }
            set { myTimeElapsed = value; this.NotifyPropertyChanged("MyTimeElapsed"); }
        }


        private Record myRecord = null;

        public Record MyRecord
        {
            get { return myRecord; }
            set { myRecord = value; }
        }

        #endregion

        #region Constructors
        /// <summary>
        /// Main constructor
        /// </summary>
        public MainWindow()
        {

            InitializeComponent();
            
            //FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(Thread.CurrentThread.CurrentCulture.IetfLanguageTag)));
            System.Windows.Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
            this.Closing += new CancelEventHandler(MainWindow_Closing);             
            //set initial datacontext for UI controls             
            this.recordsListView.DataContext = this;
            this.workHistoryChart.DataContext = this;
            this.workHistoryChartPopup.DataContext = this;
            this.workTypeComboBox.DataContext = this;
            this.commentsTextBox.DataContext = this;
            this.startTimeLabel.DataContext = this;
            this.timeElapsedLabel.DataContext = this;
            var assembVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
            this.Title = "WorkRecorder v" + assembVersion.Major + "." + assembVersion.Minor;
            timer.Interval = new TimeSpan(0, 0, 0, 1);
            timer.Tick += timer1_Tick;

            //default time for time started and ended
            this.MyStartTime = DateTime.Now;
            
            //load records - if clickone deplayed, else
            if (ApplicationDeployment.IsNetworkDeployed)
            {
                DeploymentPath = ApplicationDeployment.CurrentDeployment.DataDirectory + "/";
                RecordsPath = DeploymentPath + ConfigurationManager.AppSettings["workHistory"];
                
            }
            else
            {

                var path = System.IO.Path.GetFullPath(ConfigurationManager.AppSettings["workHistory"]);
                RecordsPath = path;
            }

            //load work history
            LoadDataStore();

        }

        #endregion




        #region Pie chart

        /// <summary>
        /// 
        /// </summary>
        private void LoadChartData()
        {
            History.Clear();
            WorkHistory wh;

            foreach (var r in RecordSet)
            {
                //see if work history contains work type, if none found, add all of
                //that type to work history
                var items = History.Where(c => c.WorkType == r.RType).Count();
                if (items == 0)
                {
                    var matchingTypes = from t in RecordSet
                                        where t.RType == r.RType
                                        select t;


                    wh = new WorkHistory();
                    wh.WorkType = r.RType;
                    wh.WorkTypeCount = matchingTypes.Count();
                    history.Add(wh);
                }

            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void pieChart_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            chartPopup.IsOpen = true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void chartPopup_MouseLeave(object sender, MouseEventArgs e)
        {
            CloseChartPopup();
        }

        /// <summary>
        /// 
        /// </summary>
        private void CloseChartPopup()
        {
            this.chartPopup.IsOpen = false;

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void popupCloseButton_Click(object sender, RoutedEventArgs e)
        {
            CloseChartPopup();
        }

        #endregion

        #region Worker methods

 
        private void RestartProgram()
        {
        
            //networkdeployed (clickonce) get path of start menu shortcut
            if (ApplicationDeployment.IsNetworkDeployed)
            {
                System.Reflection.Assembly assembly = System.Reflection.Assembly.GetEntryAssembly();
                var companyNameAtts = assembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
                var productNameAtts = assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false);

                string productName = string.Empty;
                string companyName = string.Empty;
                //company name  
                if (companyNameAtts != null && companyNameAtts.Length > 0)
                {
                    companyName = ((AssemblyCompanyAttribute)companyNameAtts[0]).Company;
                }
                //product name
                if (productNameAtts != null && productNameAtts.Length > 0)
                {
                    productName = ((AssemblyProductAttribute)productNameAtts[0]).Product;
                }
                //string shortcutFileName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), "Start TestApp.lnk");
                string allProgramsPath = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
                string shortcutPath = System.IO.Path.Combine(allProgramsPath, companyName);
                shortcutPath = System.IO.Path.Combine(shortcutPath, productName) + ".appref-ms";
                System.Diagnostics.Process.Start(shortcutPath);
            }
            else
            {                
                Process.Start(Application.ResourceAssembly.Location);
            }
       
            Environment.Exit(0);
        }

        private bool ExitProgram()
        {
            if (ShowMessageBox(this.FindResource("messageTitleWarning").ToString(), this.FindResource("messageExitProgram").ToString(), MessageBoxButton.YesNo))
            {
                
                return true;
            }
            return false; 
        }

        /// <summary>
        /// Load work history data store
        /// </summary>
        private void LoadDataStore() 
        { 
            try
            {
                RecordManager rm = new RecordManager();
                var r = rm.GetRecords(@RecordsPath);
                if (r != null) { RecordSet = r; } else { ShowMessageBox(this.FindResource("messageTitleError").ToString(), this.FindResource("messageMissingDataFile").ToString(), MessageBoxButton.OK); }
            }
            catch (Exception e)
            {
                //need error framework built, with error number dictionary,
                //so that an error log can be created
                ShowMessageBox(this.FindResource("messageUnknownException").ToString(), "Can't read file from: "+RecordsPath +"\nException: "+ e.Message, MessageBoxButton.OK);
            }

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="title"></param>
        /// <param name="msg"></param>
        /// <param name="buttonType"></param>
        /// <returns></returns>
        private bool ShowMessageBox(string title, string msg, MessageBoxButton buttonType)
        {
            MessageBoxResult result = MessageBox.Show(msg, title, buttonType);
            if (result == MessageBoxResult.No) { return false; }
            else if (result == MessageBoxResult.Cancel) { return false; }
            return true;
        }
        
        /// <summary>
        /// 
        /// </summary>
        private void SaveRecords()
        {
            RecordManager rm = new RecordManager();
            rm.SaveRecords(RecordSet, @RecordsPath.ToString());
        }

        #endregion

        #region UI event methods
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void loadHistoryButton_Click(object sender, RoutedEventArgs e)
        {
            // ShowMessageBox("Warning", "are you sure you want to delete all records?", MessageBoxButton.YesNo);
            LoadDataStore();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void editCommentsSaveButton_Click(object sender, RoutedEventArgs e)
        {
            SaveRecords();
        }

        private void loadRecordButton_Click(object sender, RoutedEventArgs e)
        {
            var context = ((sender as FrameworkElement).DataContext) as ListViewItem;
            var contextRecord = context.DataContext as Record;

            //find record in collection
            var r = RecordSet.Single(x => x == contextRecord) as Record;

            //var xmlDp = this.FindResource("workTypesDataProvider") as XmlDataProvider;


            object item = null;
            var items = this.workTypeComboBox.Items;
            foreach (var i in items)
            {
                if ((i as XmlAttribute).Value == r.RType)
                {
                    item = i;
                }

            }
             
            this.workTypeComboBox.SelectedIndex =  this.workTypeComboBox.Items.IndexOf(item);;
            this.MyWorkType = item as XmlAttribute;
            this.MyStartTime = r.StartTime;
            this.MyComments = r.Comments;
            this.MyTimeElapsed = new TimeSpan(r.TimeElapsed);
            this.MyRecord = r;
            this.canSaveRecord = true;
             
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void deleteRecord_Click(object sender, RoutedEventArgs e)
        {
            var context = ((sender as FrameworkElement).DataContext) as ListViewItem;
            var r = context.DataContext as Record;

            if (ShowMessageBox(this.FindResource("messageTitleWarning").ToString(), this.FindResource("messageDeleteRecord").ToString(), MessageBoxButton.YesNo))
            {
                //remove from record set
                RecordSet.Remove(r);

                //reset data context so row number converter will reapply (rebind to RecordSet),
                //otherwise row numbers are not recounted and they become out of order
                //e.g. record 0,1,2, removing 1 leaves row numbers of 0,2 showing
                this.recordsListView.DataContext = null;
                this.recordsListView.DataContext = this;

                LoadChartData();
                SaveRecords();
            }
        }
 
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void deleteAllButton_Click(object sender, RoutedEventArgs e)
        {
            if (ShowMessageBox(this.FindResource("messageTitleWarning").ToString(), this.FindResource("messageDeleteAllRecords").ToString(), MessageBoxButton.YesNo))
            {
                RecordSet.Clear();
                SaveRecords(); 
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void about_Click(object sender, RoutedEventArgs e)
        {
            //Application.Current.MainWindow.Resources.MergedDictionaries
                //App.Current.MainWindow.Resources.MergedDictionaries
            var dicts = Application.Current.Resources.MergedDictionaries;
            About a = new About();
             

            
            //add theme resource dictinary if exists
            //if (dicts.Count > 0)
            //{
            //    a.Resources.MergedDictionaries.Add(dicts[1]);
            //}
            a.ShowDialog();
            //a.Show();            
            a.Owner = this;

        }

        /// <summary>
        /// Close window event 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void MainWindow_Closing(object sender, CancelEventArgs e)
        {
            //if yes to close program, continue exit program by e.cancel false, else cancel is true
            if (ExitProgram()) { e.Cancel = false; Environment.Exit(0); } else { e.Cancel = true; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void exitProgram_Click(object sender, RoutedEventArgs e)
        {
            MainWindow_Closing(this, new CancelEventArgs());
            
        }
        #endregion





 
    }

    #region routed UI commands class and custom commands
    /// <summary>
    /// RoutedUICommands class
    /// </summary>
    public static class MyCustomCommand
    {
        public static readonly RoutedUICommand CanStartCommand = new RoutedUICommand("Can Start Command", "CanStartCommand", typeof(MyCustomCommand));
        public static readonly RoutedUICommand CanStopCommand = new RoutedUICommand("Can Stop Command", "CanStopCommand", typeof(MyCustomCommand));
        public static readonly RoutedUICommand CanClearCommand = new RoutedUICommand("Can Clear Command", "CanClearCommand", typeof(MyCustomCommand));
        public static readonly RoutedUICommand CanSaveCommand = new RoutedUICommand("Can Save Command", "CanSaveCommand", typeof(MyCustomCommand));
        public static readonly RoutedUICommand LanguageItemSelectionCommand = new RoutedUICommand("Language Item Selection Command", "LanguageItemSelectionCommand", typeof(MyCustomCommand));
        public static readonly RoutedUICommand ThemeItemSelectionCommand = new RoutedUICommand("Theme Item Selection Command", "ThemeItemSelectionCommand", typeof(MyCustomCommand));
    }
    #endregion

}
